package caidan.data;

import caidan.MainApp;
import jinyilw.common.Actor;
import jinyilw.common.ActorSets;
import jinyilw.common.Operator;
import jinyilw.common.OperatorSets;
import jinyilw.common.SettingTools;
import jinyilw.common.TextTools;
import jinyilw.common.app.AppConfig;
import jinyilw.common.file.FileTools;
import jinyilw.common.math.MathTools;
import jinyilw.swing.image.ImageTools;

import javax.swing.JOptionPane;
import java.awt.Component;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

public class CategoryData
{
	public static final File cDataFolder = FileTools.getUserFile("cData");
	public static final String isMainFoodKey = "isMainFood";
	public static final String isMainDishesKey = "isMainDishes";
	ArrayList<FoodData> allDatas = new ArrayList<FoodData>();
	ArrayList<FoodData> breakfastDatas = new ArrayList<FoodData>();
	ArrayList<FoodData> otherDatas = new ArrayList<FoodData>();
	private final ActorSets<Integer, Boolean> dataDeletedActors = new ActorSets<>();
	private final ActorSets<FoodData, Boolean> dataAddedActors = new ActorSets<>();
	private final OperatorSets<String> nameChangedOperators = new OperatorSets<>();
	String categoryName;
	int breakfastChooseNum, otherChooseNum;
	boolean isMainFood;
	boolean isMainDishes;
	private boolean isEdited, isNameChanged;
	private final File dataFile;
	File breakfastFolder, otherFolder;
	int categoryID, breakfastEditID, otherEditID;
	int keyIndex;

	public CategoryData(String cName, int breakfastChooseN, int otherChooseN,
			int kIndex, int id)
	{
		categoryName = cName;
		breakfastChooseNum = breakfastChooseN;
		otherChooseNum = otherChooseN;
		keyIndex = kIndex;
		isMainFood = SettingTools.getDefaultBool(isMainFoodKey + keyIndex,
				MainApp.isMainFoods[keyIndex]);
		isMainDishes = SettingTools.getDefaultBool(isMainDishesKey + keyIndex,
				MainApp.isMainDishes[keyIndex]);
		categoryID = breakfastEditID = otherEditID = id;

		otherFolder = new File(cDataFolder, cName);
		breakfastFolder = new File(otherFolder, "早餐");

		dataFile = new File(otherFolder, ".menudata");
		if (dataFile.isFile())
			initFromFile();
		else
			initFromForder();
	}

	private void initFromFile()
	{
		readDatas(allDatas, dataFile);
		for (FoodData foodData : allDatas)
		{
			if (foodData.isInBreakfast())
				breakfastDatas.add(foodData);
			if (foodData.isInOther())
				otherDatas.add(foodData);
		}
	}

	private void initFromForder()
	{
		String fileName, name;
		FoodData foodData;
		if (breakfastFolder != null && breakfastFolder.isDirectory())
		{
			for (File file : breakfastFolder.listFiles())
			{
				if (file.isFile())
				{
					fileName = file.getName();
					if (ImageTools.isNormalImage(fileName))
					{
						foodData = new FoodData(file, fileName, this);
						foodData.setInBreakfast(true);
						allDatas.add(foodData);
						breakfastDatas.add(foodData);
					}
				}
			}
			isEdited = true;
		}

		if (otherFolder != null && otherFolder.isDirectory())
		{
			for (File file : otherFolder.listFiles())
			{
				if (file.isFile())
				{
					fileName = file.getName();
					name = TextTools.getFileNameNoExt(fileName);
					foodData = FoodData.getData(breakfastDatas, name);
					if (foodData != null)
						foodData.setInOther(true);
					else if (ImageTools.isNormalImage(fileName))
					{
						foodData = new FoodData(file, fileName, name, this);
						foodData.setInOther(true);
						allDatas.add(foodData);
					}
				}
			}
			isEdited = true;
		}

		allDatas.sort(null);
		breakfastDatas.clear();
		for (FoodData fData : allDatas)
		{
			if (fData.isInBreakfast())
				breakfastDatas.add(fData);
			if (fData.isInOther())
				otherDatas.add(fData);
		}
	}

	public ArrayList<FoodData> getAllDatas()
	{
		return allDatas;
	}

	public ArrayList<FoodData> getCurrentDatas(boolean isBreakfast)
	{
		if (isBreakfast)
			return breakfastDatas;
		return otherDatas;
	}

	//	public File getCurrentForder(boolean isBreakfast)
	//	{
	//		if (isBreakfast)
	//			return breakfastFolder;
	//		return otherFolder;
	//	}

	public String getCategoryName()
	{
		return categoryName;
	}

	public int getBreakfastChooseNum()
	{
		return breakfastChooseNum;
	}

	public int getOtherChooseNum()
	{
		return otherChooseNum;
	}

	public boolean isMainFood()
	{
		return isMainFood;
	}

	public boolean isMainDishes()
	{
		return isMainDishes;
	}

	public int getCategoryID()
	{
		return categoryID;
	}

	public int getEditID(boolean isBreakfast)
	{
		if (isBreakfast)
			return breakfastEditID;
		return otherEditID;
	}

	public void setEditID(boolean isBreakfast, int id)
	{
		if (isBreakfast)
			breakfastEditID = id;
		else
			otherEditID = id;
	}

	public void setEdited(boolean b)
	{
		isEdited = b;
	}

	public void setCategoryName(String name)
	{
		if (categoryName != name && !TextTools.isBlank(name))
		{
			categoryName = name;
			isNameChanged = true;
			nameChangedOperators.operate(name);
			MainApp.setCategoryName(name, keyIndex);
		}
	}

	public void setMainFood(boolean b)
	{
		if (isMainFood != b)
		{
			isMainFood = b;
			SettingTools.putDefaultSettingData(isMainFoodKey + keyIndex, b);
		}
	}

	public void setMainDishes(boolean b)
	{
		if (isMainDishes != b)
		{
			isMainDishes = b;
			SettingTools.putDefaultSettingData(isMainDishesKey + keyIndex, b);
			FoodAssorter.calculateMainDishIndexes();
		}
	}

	public int[] findFavorite()
	{
		return findFavorites(allDatas);
	}

	public int[] findFavorite(int day, int meal)
	{
		if (meal == 0)
			return findFavorites(breakfastDatas, day, meal);
		return findFavorites(otherDatas, day, meal);
	}

	public static int[] findFavorites(ArrayList<FoodData> list, int d, int m,
			int num)
	{
		int[] numbers = new int[list.size()];
		for (int i = 0; i < numbers.length; i++)
			numbers[i] = list.get(i).foodCount.getCount(d, m);
		int[] results = MathTools.getDecreaseIndexs(num, numbers);
		int i = 0;
		for (; i < results.length; i++)
			if (numbers[results[i]] == 0)
				break;
		for (; i < results.length; i++)
			results[i] = -1;
		return results;
	}

	public static int[] findFavorites(ArrayList<FoodData> foodDatas, int d,
			int m)
	{
		int[] numbers = new int[foodDatas.size()];
		for (int i = 0; i < numbers.length; i++)
			numbers[i] = foodDatas.get(i).foodCount.getCount(d, m);
		int[] indexs = MathTools.getDecreaseIndexs(numbers);
		int i = 0;
		for (; i < indexs.length; i++)
			if (numbers[i] == 0)
				break;
		if (i == numbers.length)
			return indexs;
		if (i == 0)
			return null;
		int[] results = new int[i + 1];
		for (i = 0; i < results.length; i++)
			results[i] = indexs[i];
		return results;
	}

	//	public static int[] findFavorites(ArrayList<FoodData> list, int d, int m)
	//	{
	//		int[] numbers = new int[list.size()];
	//		for (int i = 0; i < numbers.length; i++)
	//			numbers[i] = list.get(i).foodCount.getCount(d, m);
	//		int[] indexs = MathTools.getDecreaseIndex(numbers);
	//		int i = 0;
	//		for (; i < indexs.length; i++)
	//			if (numbers[i] == 0)
	//				break;
	//		for (; i < indexs.length; i++)
	//			indexs[i] = -1;
	//		return indexs;
	//	}

	public static int[] findFavorites(ArrayList<FoodData> list)
	{
		int[] numbers = new int[list.size()];
		for (int i = 0; i < numbers.length; i++)
			numbers[i] = list.get(i).foodCount.getTotalCount();
		return MathTools.getDecreaseIndexs(numbers);
	}

	public void writeCountData(DataOutputStream dos)
	{
		int listSize = allDatas.size();
		FoodData foodData;
		FoodCount foodCount;
		try
		{
			dos.writeInt(listSize);
			for (int i = 0; i < listSize; i++)
			{
				foodData = allDatas.get(i);
				dos.writeUTF(foodData.getFoodName());
				foodCount = foodData.getFoodCount();
				dos.writeInt(foodCount.getTotalCount());
				int[] counts = foodCount.getMealCount();
				for (int j = 0; j < counts.length; j++)
					dos.writeInt(counts[j]);
			}
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public void readCountData(DataInputStream dis) throws IOException
	{
		FoodData foodData;
		String foodName;
		FoodCount foodCount;
		int size = dis.readInt();
		for (int i = 0; i < size; i++)
		{
			foodName = dis.readUTF();
			foodData = FoodData.getData(allDatas, foodName);
			if (foodData != null)
			{
				foodCount = foodData.getFoodCount();
				foodCount.setTotalCount(dis.readInt());
				int[] counts = foodCount.getMealCount();
				for (int j = 0; j < counts.length; j++)
					counts[j] = dis.readInt();
			} else
			{
				dis.readInt();
				for (int j = 0; j < 21; j++)
					dis.readInt();
			}
		}
	}

	public void readAndCombineCountData(DataInputStream dis) throws IOException
	{
		FoodData foodData;
		String foodName;
		FoodCount foodCount;
		int size = dis.readInt();
		for (int i = 0; i < size; i++)
		{
			foodName = dis.readUTF();
			foodData = FoodData.getData(allDatas, foodName);
			if (foodData != null)
			{
				foodCount = foodData.getFoodCount();
				foodCount.setTotalCount(
						foodCount.getTotalCount() + dis.readInt());
				int[] counts = foodCount.getMealCount();
				for (int j = 0; j < counts.length; j++)
					counts[j] += dis.readInt();
			} else
			{
				dis.readInt();
				for (int j = 0; j < 21; j++)
					dis.readInt();
			}
		}
	}

	public void clearData()
	{
		FoodCount fc;
		int size = allDatas.size();
		for (int i = 0; i < size; i++)
		{
			fc = allDatas.get(i).getFoodCount();
			fc.setTotalCount(0);
			int[] counts = fc.getMealCount();
			for (int j = 0; j < counts.length; j++)
				counts[j] = 0;
		}
	}

	public void reset()
	{
		FileTools.deleteFolder(new File(breakfastFolder, ".thumb"));
		FileTools.deleteFolder(new File(otherFolder, ".thumb"));
		if (dataFile != null)
			dataFile.delete();
	}

	public void saveWhenExit()
	{
		if (isEdited)
		{
			dataFile.delete();
			writeDatas(allDatas, dataFile);
		}
		if (isNameChanged)
			otherFolder.renameTo(new File(cDataFolder, categoryName));
	}

	private void writeDatas(ArrayList<FoodData> foodDatas, File file)
	{
		try (DataOutputStream dos = new DataOutputStream(
				new FileOutputStream(file)))
		{
			dos.writeInt(AppConfig.appVersion);
			dos.writeInt(foodDatas.size());
			for (FoodData foodData : foodDatas)
				foodData.writeData(dos);
			dos.close();
			FileTools.setToHidden(file);
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	private void readDatas(ArrayList<FoodData> foodDatas, File file)
	{
		try (DataInputStream dis = new DataInputStream(
				new FileInputStream(file)))
		{
			FoodData foodData;
			int appVersion = dis.readInt();
			if (appVersion < 305)
			{
				dis.close();
				readOldDatas();
				return;
			}
			int size = dis.readInt();
			for (int i = 0; i < size; i++)
			{
				foodData = new FoodData(this);
				foodData.readData(dis, appVersion);
				foodDatas.add(foodData);
			}
			dis.close();
		} catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	private void readOldDatas()
	{
		isEdited = true;
		File breakfastDataFile = new File(breakfastFolder, ".menudata");
		String[] fileNames;
		String name;
		FoodData foodData;

		fileNames = readStrings(breakfastDataFile);
		if (fileNames != null)
			for (String fileName : fileNames)
			{
				foodData = new FoodData(new File(breakfastFolder, fileName),
						fileName, this);
				foodData.setInBreakfast(true);
				allDatas.add(foodData);
				breakfastDatas.add(foodData);
			}

		fileNames = readStrings(dataFile);
		if (fileNames != null)
			for (String fileName : fileNames)
			{
				name = TextTools.getFileNameNoExt(fileName);
				foodData = FoodData.getData(breakfastDatas, name);
				if (foodData != null)
					foodData.setInOther(true);
				else
				{
					foodData = new FoodData(new File(otherFolder, fileName),
							fileName, name, this);
					foodData.setInOther(true);
					allDatas.add(foodData);
				}
			}

		allDatas.sort(null);
		breakfastDatas.clear();
	}

	private String[] readStrings(File file)
	{
		String[] strings = null;
		DataInputStream dis = null;
		try
		{
			dis = new DataInputStream(new FileInputStream(file));
			int size = dis.readInt();
			strings = new String[size];
			for (int i = 0; i < size; i++)
				strings[i] = dis.readUTF();
			dis.close();
		} catch (Exception e)
		{
			e.printStackTrace();
			if (dis != null)
				try
				{
					dis.close();
				} catch (IOException e1)
				{
					e1.printStackTrace();
				}
		}
		return strings;
	}

	public void addDataDeletedActor(Actor<Integer, Boolean> actor)
	{
		dataDeletedActors.add(actor);
	}

	public void addDataAddedActor(Actor<FoodData, Boolean> actor)
	{
		dataAddedActors.add(actor);
	}

	public void addNameChangedOperater(Operator<String> operator)
	{
		nameChangedOperators.add(operator);
	}

	public void delete(int index, boolean isBreakfast)
	{
		ArrayList<FoodData> datas = isBreakfast ? breakfastDatas : otherDatas;
		FoodData data = datas.get(index);
		data.delete(isBreakfast);
		if (data.isInNone())
			allDatas.remove(data);
		datas.remove(index);
		isEdited = true;
		dataDeletedActors.act(index, isBreakfast);
	}

	public boolean addNew(File imgFile, String foodName, boolean isBreakfast,
			Component com)
	{
		ArrayList<FoodData> chooseDatas, unchooseDatas;
		File copyForder;

		if (isBreakfast)
		{
			chooseDatas = breakfastDatas;
			unchooseDatas = otherDatas;
			copyForder = breakfastFolder;
		} else
		{
			chooseDatas = otherDatas;
			unchooseDatas = breakfastDatas;
			copyForder = otherFolder;
		}

		if (FoodData.getIndex(chooseDatas, foodName) != -1)
		{
			JOptionPane.showMessageDialog(com, foodName + " 已存在! 不能重复添加！",
					"添加新菜单", JOptionPane.OK_OPTION);
			return false;
		}
		String fileName = foodName + "."
				+ TextTools.getFileExt(imgFile.getAbsolutePath());
		File copyImgFile = new File(copyForder, fileName);
		FileTools.copyFile(imgFile, copyImgFile);
		FoodData foodData = FoodData.getData(unchooseDatas, foodName);
		if (foodData != null)
		{
			foodData.setInBoth();
		} else
		{
			foodData = new FoodData(copyImgFile, fileName, foodName, this);
			if (isBreakfast)
				foodData.setInBreakfast(true);
			else
				foodData.setInOther(true);
			allDatas.add(foodData);
		}
		chooseDatas.add(foodData);
		dataAddedActors.act(foodData, isBreakfast);
		return true;
	}
}
